/*###################################################################
  > File Name: storage/storage/local_vol.c
  > Author: Vurtune
  > Mail: vurtune@foxmail.com
  > Created Time: Mon 15 May 2017 10:18:43 PM PDT
###################################################################*/
#include "config.h"

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>

#include "sysy_lib.h"
#include "cluster.h"
#include "node.h"
#include "disk.h"
#include "net_global.h"
#include "ylog.h"
#include "dbg.h"
#include "../task/utils.h"
#include "lich_md.h"

#define LOCAL_VOL_SYNC_INTERVAL (60*60*24)
#define LOCAL_VOL_SYNC_STEP 10

static BOOL inited = FALSE,
            synced = FALSE;
static sem_t sync_sem;

void local_vol_sync_post(BOOL force)
{
        if (inited == FALSE) {
                return;
        }

        if (!force && synced) {
                return;
        }

        sem_post(&sync_sem);

        synced = TRUE;
}

typedef struct {
        struct list_head hook;
        chkid_t vol;
        char pool[MAX_NAME_LEN];
} entry_t;

typedef struct {
        struct list_head list;
        int count;
} vol_list;

static void __disk_load_vol_md__(void *_arg, void *_ent, void *_pool)
{
        int ret;
        vol_list* vols = _arg;
        const chkid_t *chkid = _ent;
        entry_t *ent = NULL;

        if (is_volume(chkid)) {
                ret = ymalloc((void **)&ent, sizeof(entry_t));
                if (unlikely(ret)) {
                        goto out;
                }
                ent->vol = *chkid;
                strcpy(ent->pool, (char *)_pool);

                vols->count ++;
                list_add_tail(&ent->hook, &vols->list);
        }

out:
        return;
}

static BOOL __is_local_vol(const char *pool, chkid_t *chkid)
{
        int ret, retry = 0, deleted = 0;

        char _chkinfo[CHKINFO_MAX];
        chkinfo_t *chkinfo = (void *)_chkinfo;

        if (is_volume(chkid)) {
                return FALSE;
        }
retry:
        ret = md_chunk_getinfo(pool, NULL, chkid, chkinfo, NULL);
        if (unlikely(ret)) {
                if (ret == ENOENT) {
                        return FALSE;
                } else {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                }
        }

        ret = vol_is_deleting(chkid, &deleted);
        if (unlikely(ret)) {
                return FALSE;
        }

        if (!net_islocal(CHKINFO_FIRSTNID(chkinfo)) && deleted) {
                return FALSE;
        }

        return TRUE;
err_ret:
        return FALSE;

}

static void *__local_vol_sync(void *arg)
{
        int ret, count = 0;
        local_vol_t *local_vols;
        local_vol_opt_t opt;
        vol_list md_list;
        struct list_head *pos = NULL, *n = NULL;
        entry_t *ent = NULL;

        (void) arg;
        INIT_LIST_HEAD(&md_list.list);

        while (srv_running) {
                _sem_timedwait1(&sync_sem, LOCAL_VOL_SYNC_INTERVAL);

                count = 0;
                ret = ymalloc((void **)&local_vols, sizeof(local_vol_t) +
                                sizeof(chkid_t) * LOCAL_VOL_SYNC_STEP);
                if (unlikely(ret)) {
                        continue;
                }

                disk_maping->iterator("metadata", __disk_load_vol_md__, &md_list);

                opt = LOCAL_VOL_SYNC;
                list_for_each_safe(pos, n, &md_list.list) {
                        ent = (entry_t *)pos;
                        if (__is_local_vol(ent->pool, &ent->vol))
                        {
                                local_vols->vols[count++] = ent->vol;
                        }

                        list_del(pos);
                        yfree((void **)&pos);

                        if (list_empty(&md_list.list) || count == LOCAL_VOL_SYNC_STEP) {
                                local_vols->volcount = count;
                                count = 0;

                                dispatch_vol_notifiction(__node__.name, local_vols, opt);
                                opt = LOCAL_VOL_ADD;
                        }
                }

                yfree((void **)&local_vols);
        }

        return NULL;
}

int local_vol_sync_bh_init()
{
        int ret;
        ret = sem_init(&sync_sem, 0, 0);
        if (ret < 0) {
                ret = errno;
        }

        sy_thread_create(__local_vol_sync, NULL);

        inited = TRUE;

        return ret;
}

